<?php

namespace app\utils\signApi;
// 采坑记录, curl 设置协议为文本数组,不是键值对数组,
// 采坑记录, curl 协议头键名不能带 _ 否则接收方收不到该值
// 采坑记录, curl 协议头键名会被自动转首字母大写,后续小写, 所以单词要用 -  分割  Lt-Sign-Id 键名也要这个规则,不然拼接时会错误(已改键名不参与拼接)
class signV1
{
    public $secretId = ""; //安全id
    public $secretKey = ""; //安全key
    public $err = "";

    function __construct($secretId = null, $secretKey = null)
    {
        if ($secretId) {
            $this->secretId = $secretId;
        }
        if ($secretKey) {
            $this->secretKey = $secretKey;
        }
    }

    // 获取sign 需要在协议头添加的数据
    function getSignV1($data): array
    {
        $header = array();
        $signTime = time();
        //添加必要参数 因为协议头会自动被转化所大小写以单独处理
        $header["Lt-Sign-Version"] = 'v1';
        $header["Lt-Secret-Id"] = $this->secretId;
        $header["Lt-Timestamp"] = $signTime;
        // 对数组的值按key排序
        $data = array_merge($data, $header);

        //多维数组内排序
        $sortFunc = static function (array $params, callable $sortFunc): array {
            array_walk($params, static function (&$item) use ($sortFunc) {
                $item = is_array($item) ? $sortFunc($item, $sortFunc) : $item;
            });
            ksort($params);
            return $params;
        };
        $data = $sortFunc($data, $sortFunc);
        //因为键名有被自动转换成首字母大写,或小写的情况,比如获取所有请求头,就会被自动转首字母大写后面小写,所以只校验值
        $params = implode('', $data);
        $params = $params . $this->secretKey;
        // 生成sign
        $sign = md5($params);
        $this->temp = $params . $this->secretKey;
        $header["Lt-Sign"] = $sign;
        if (env()->get("APP_debug") == 1) {
            $header["Lt-params"] = $params;
        }
        return $header;
    }

    ///后台验证sign是否合法
    function verifySignV1($header, $data): bool
    {
        // 验证参数中是否有签名
        if (!isset($header['Lt-Sign-Version']) || !$header['Lt-Sign-Version']) {
            $this->err = 'Lt-Sign-Version不存在';
            return false;
        }

        // 验证参数中是否有签名
        if (!isset($header['Lt-Sign']) || !$header['Lt-Sign']) {
            $this->err = 'Lt-Sign不存在';
            return false;
        }
        if (!isset($header['Lt-Timestamp']) || !$header['Lt-Timestamp']) {
            $this->err = 'Lt-Timestamp不存在';
            return false;
        }
        // 验证请求， 10分钟失效
        if (time() - $header['Lt-Timestamp'] > 600) {
            $this->err = 'Lt-Timestamp封包超时';
            return false;
        }
        $tempHeader = array();
        //添加必要参数
        $tempHeader["Lt-Sign-Version"] = 'v1';
        $tempHeader["Lt-secret-Id"] = $this->secretId;
        $tempHeader["Lt-Timestamp"] = $header['Lt-Timestamp'];

        // 对数组的值按key排序
        $data = array_merge($data, $tempHeader);

        //多维数组内排序
        $sortFunc = static function (array $params, callable $sortFunc): array {
            array_walk($params, static function (&$item) use ($sortFunc) {
                $item = is_array($item) ? $sortFunc($item, $sortFunc) : $item;
            });
            ksort($params);
            return $params;
        };
        $data = $sortFunc($data, $sortFunc);
        //因为键名有被自动转换成首字母大写,或小写的情况,比如获取所有请求头,就会被自动转首字母大写后面小写,所以只校验值
        $params = implode('', $data);
        // 生成sign
        $sign = md5($params . $this->secretKey);
        if ($sign == $header['Lt-Sign']) {
            return true;
        } else {
            $this->err = '签名错误';
            if (env()->get("APP_debug", "") == 1) {
                $this->err = '签名错误验证拼接数据:' . $params . $this->secretKey;
            }
            return false;
        }
    }

    /**
     * 发送curl请求
     */
    public function send($paramObj = "")
    {
        $dataObj = paramsObj();
        $dataObj->request = paramsObj();
        $dataObj->data = paramsObj();
        $dataObj->result = array();

        $final_arr = logInit();
        if ($final_arr["error_no"] == 0) {
            //参数验证
            $mixed = parseToArr($paramObj, "mixed", 0);
            $params = parseToArr($paramObj, "params", array());
            $messages = parseToArr($paramObj, "messages", array());
            $rules = array(
                'submitUrl' => 'require',
                'method' => '',
                'time' => '',
                'data' => '',
                'headers' => '',
                'cookies' => '',
                'isReturnHeader' => '',
            );
            $dataDefault = array();
            $dataDefault["method"] = "get";
            $dataDefault["time"] = 6;
            $dataDefault["data"] = array();
            $dataDefault["headers"] = array();
            $dataDefault["cookies"] = "";
            $dataDefault["isReturnHeader"] = 0;
            $paramsObj = paramsObj();
            $paramsObj->params = $params;
            $paramsObj->mixed = $mixed;
            $paramsObj->rules = $rules;
            $paramsObj->messages = $messages;
            $paramsObj->dataDefault = $dataDefault;
            $checkResult = app("verifyParam")::validatorRequest($paramsObj);
            if ($checkResult["error_no"] == 0) {
                $dataObj->request = $checkResult["result"];
            } else {
                $final_arr = logCallErrorMsg($final_arr, $checkResult);
            }
        }
        if ($final_arr["error_no"] == 0) {
            $curl = curl_init($dataObj->request->submitUrl);
            curl_setopt($curl, CURLOPT_CUSTOMREQUEST, strtoupper($dataObj->request->method));
            if ("https://" == substr($dataObj->request->submitUrl, 0, 8)) {
                curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
                curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
            }
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
            curl_setopt($curl, CURLOPT_FAILONERROR, false);
            curl_setopt($curl, CURLOPT_TIMEOUT, $dataObj->request->time);// 单位 秒，超时限制。
            if ($dataObj->request->isReturnHeader) {
                curl_setopt($curl, CURLOPT_HEADER, true); // 返回HTTP头
            } else {
                curl_setopt($curl, CURLOPT_HEADER, false); // 过滤HTTP头
            }
            if ($dataObj->request->cookies) {
                curl_setopt($curl, CURLOPT_COOKIE, $dataObj->request->cookies); // 带上COOKIE请求
            }
            if (strtolower($dataObj->request->method) == "post") {
                curl_setopt($curl, CURLOPT_POST, true); // post传输数据
            }
            if ($dataObj->request->data) {
                if (is_array($dataObj->request->data)) {
                    curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($dataObj->request->data));
                } else {
                    curl_setopt($curl, CURLOPT_POSTFIELDS, $dataObj->request->data);
                }
            }
            //处理签名=添加签名协议头=======================
            $data = array();
            //整理出params数据
            if (strtolower($dataObj->request->method) == "get") {
                // 解析 URL 查询字符串
                parse_str(parse_url($dataObj->request->submitUrl, PHP_URL_QUERY), $data);
            } else if (strtolower($dataObj->request->method) == "post") {
                if (is_array($dataObj->request->data)) {
                    $data = $dataObj->request->data;
                } else {
                    //先简单验证是否是json格式
                    if (substr(trim($dataObj->request->data), 0, 1) === '{' && substr(trim($dataObj->request->data), -1) === '}') {
                        $data = json_decode($dataObj->request->data);
                        if (json_last_error() != JSON_ERROR_NONE) {
                            parse_str($dataObj->request->data, $data); // 解析字符串数据为数组
                        }
                    } else {
                        parse_str($dataObj->request->data, $data); // 解析字符串数据为数组
                    }
                }
            }

            $Headers2 = $this->getSignV1($data);
            $auth2 = $this->verifySignV1($Headers2, $data);
            $arr = array();
            foreach ($Headers2 as $key => $val) {
                $arr[] = $key . ": " . $val;
            }
            $dataObj->request->headers = array_merge($dataObj->request->headers, $arr);
            //===========处理签名结束=============================================
            if ($dataObj->request->headers) {
                curl_setopt($curl, CURLOPT_HTTPHEADER, $dataObj->request->headers);
            }
            $sContent = curl_exec($curl);
            $errno = curl_errno($curl);
            $error_msg = curl_error($curl);
            $requestHeader = curl_getinfo($curl);
            $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); //获取响应状态码

            if ($dataObj->request->isReturnHeader) {
                $headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE); //获取头信息大小
                $header = substr($sContent, 0, $headerSize);
                $body = substr($sContent, $headerSize);
            } else {
                $header = "";
                $body = $sContent;
            }
            curl_close($curl);
            if ($errno == 0) {
                $return = array();
//                $return['params']=$dataObj->request->data;
//                $return['requestHeader']=$requestHeader;
//                $return['responseHeader']=$header;
                $return['errno'] = $errno;
                $return['error_msg'] = $error_msg;
                $return['httpCode'] = $httpCode;
                $return['body'] = $body;
//                $return['sContent']=$sContent;
                $final_arr["result"] = $return;
            } else {
                $final_arr = recordLogMsgLevelTwo(app("errorLevel")::E_WARNING, app("errorCode")::OP_FAIL, lang("sys.opFail"), $error_msg);
            }
        }
        return $final_arr;
    }


}


